<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js" lang="en">
<!--<![endif]-->
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Operations on immutable entities - The .NET Core ORM Cookbook</title>
    <link rel="shortcut icon" href="favicon.ico">
    <link rel="stylesheet" href="css/theme.css" type="text/css" />
    <link rel="stylesheet" href="css/theme_colors.css" type="text/css" />
    <link rel="stylesheet" href="css/styles/vs.css">
    <link rel="stylesheet" href="css/font-awesome.4.5.0.min.css">
</head>
<body role="document">
    <div class="grid-for-nav">
        <nav data-toggle="nav-shift" class="nav-side stickynav">
            <div class="side-nav-search">
                <a href="index.htm"><i class="fa fa-home"></i> The .NET Core ORM Cookbook</a>
                <div role="search">
                    <form id="search-form" class="form" action="Docnet_search.htm" method="get">
                        <input type="text" name="q" placeholder="Search docs" />
                    </form>
                </div>
            </div>
            <div class="menu menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul>
<li class="tocentry"><a href="index.htm">Home</a>
</li>

<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="ORMs.htm">ORMs</a></span>
</li>
<li class="tocentry"><a href="FAQ.htm">FAQ</a>
</li>
<li class="tocentry">
<ul>
<li><span class="navigationgroup"><i class="fa fa-caret-down"></i> <a href="StandardCRUDscenarios.htm">Standard CRUD scenarios</a></span></li>
<li class="tocentry"><a href="SingleModelCrud.htm">Operations on a single entity type</a>
</li>
<li class="tocentry"><a href="MultipleCrud.htm">Operations on sets</a>
</li>
<li class="tocentry"><a href="ModelWithChildren.htm">Operations on a graph of multiple entity types</a>
</li>
<li class="tocentry current"><a class="current" href="Immutable.htm">Operations on immutable entities</a>
<ul class="currentrelative">
<li class="tocentry"><a href="#scenario-prototype">Scenario Prototype</a></li>

<li class="tocentry"><a href="#ado.net">ADO.NET</a></li>

<li class="tocentry"><a href="#chain">Chain</a></li>

<li class="tocentry"><a href="#dapper">Dapper</a></li>

<li class="tocentry"><a href="#dbconnector">DbConnector</a></li>

<li class="tocentry"><a href="#entity-framework-6">Entity Framework 6</a></li>

<li class="tocentry"><a href="#entity-framework-core">Entity Framework Core</a></li>

<li class="tocentry"><a href="#linq-to-db">LINQ to DB</a></li>

<li class="tocentry"><a href="#llblgen-pro">LLBLGen Pro</a></li>

<li class="tocentry"><a href="#nhibernate">NHibernate</a></li>

<li class="tocentry"><a href="#repodb">RepoDb</a></li>

<li class="tocentry"><a href="#servicestack">ServiceStack</a></li>



</ul>
<li class="tocentry"><a href="TryCrud.htm">Handling failures and exceptions</a>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="OperationswithaForeignKeybasedLookupTable.htm">Operations with a Foreign Key based Lookup Table</a></span>
</li>

</ul>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Fetchingdatascenarios.htm">Fetching data scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Advancedscenarios.htm">Advanced scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Sortingscenarios.htm">Sorting scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Storedprocedurescenarios.htm">Stored procedure scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Auditingandhistoryscenarios.htm">Auditing and history scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="Multi-Tenancyscenarios.htm">Multi-Tenancy scenarios</a></span>
</li>
<li class="tocentry">
<span class="navigationgroup"><i class="fa fa-caret-right"></i> <a href="UnknownDatabasescenarios.htm">Unknown Database scenarios</a></span>
</li>
</ul>
				<div class="toc-footer">
					<span class="text-small">
						<hr/>
						<a href="https://github.com/FransBouma/DocNet" target="_blank">Made with <i class="fa fa-github"></i> DocNet</a>
					</span>
				</div>	
			</div>
            &nbsp;
        </nav>
        <section data-toggle="nav-shift" class="nav-content-wrap">
            <nav class="nav-top" role="navigation" aria-label="top navigation">
                <i data-toggle="nav-top" class="fa fa-bars"></i>
                <a href="index.htm">The .NET Core ORM Cookbook</a>
            </nav>
            <div class="nav-content">
                <div role="navigation" aria-label="breadcrumbs navigation">
                    <div class="breadcrumbs">
<ul><li><a href="index.htm">Home</a></li> / <li><a href="StandardCRUDscenarios.htm">Standard CRUD scenarios</a></li> / <li><a href="Immutable.htm">Operations on immutable entities</a></li></ul>
					
                    </div>
                    <hr />
                </div>
                <div role="main">
                    <div class="section">
<h1 id="crud-operations-on-immutable-objects">CRUD Operations on Immutable Objects<a class="headerlink" href="#crud-operations-on-immutable-objects" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h1>
<p>These scenarios demonstrate how to perform Create, Read, Update, and Delete operations on immutable objects. </p>
<h2 id="scenario-prototype">Scenario Prototype<a class="headerlink" href="#scenario-prototype" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<pre><code class="cs">public interface IImmutableScenario&lt;TReadOnlyModel&gt;
   where TReadOnlyModel : class, IReadOnlyEmployeeClassification
{
    /// &lt;summary&gt;
    /// Create a new EmployeeClassification row, returning the new primary key.
    /// &lt;/summary&gt;
    int Create(TReadOnlyModel classification);

    /// &lt;summary&gt;
    /// Delete a EmployeeClassification row using an object.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;Behavior when row doesn't exist is not defined.&lt;/remarks&gt;
    void Delete(TReadOnlyModel classification);

    /// &lt;summary&gt;
    /// Gets an EmployeeClassification row by its name. Assume the name is unique.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;Must return a null if when row doesn't exist.&lt;/remarks&gt;
    TReadOnlyModel? FindByName(string employeeClassificationName);

    /// &lt;summary&gt;
    /// Gets all EmployeeClassification rows.
    /// &lt;/summary&gt;
    IReadOnlyList&lt;TReadOnlyModel&gt; GetAll();

    /// &lt;summary&gt;
    /// Gets an EmployeeClassification row by its primary key.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;Behavior when row doesn't exist is not defined.&lt;/remarks&gt;
    TReadOnlyModel? GetByKey(int employeeClassificationKey);

    /// &lt;summary&gt;
    /// Update a EmployeeClassification row.
    /// &lt;/summary&gt;
    /// &lt;remarks&gt;Behavior when row doesn't exist is not defined.&lt;/remarks&gt;
    void Update(TReadOnlyModel classification);
}
</code></pre>

<pre><code class="cs">public interface IReadOnlyEmployeeClassification
{
    int EmployeeClassificationKey { get; }
    string? EmployeeClassificationName { get; }
    bool IsEmployee { get; }
    bool IsExempt { get; }
}
</code></pre>

<h2 id="ado.net">ADO.NET<a class="headerlink" href="#ado.net" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>Since ADO doesn't directly interact with models, no changes are needed for immutable objects other than to call a constructor instead of setting individual properties.</p>
<pre><code class="cs">public class ImmutableScenario : SqlServerScenarioBase, IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public ImmutableScenario(string connectionString) : base(connectionString)
    { }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        const string sql = @&quot;INSERT INTO HR.EmployeeClassification (EmployeeClassificationName, IsExempt, IsEmployee)
                    OUTPUT Inserted.EmployeeClassificationKey
                    VALUES(@EmployeeClassificationName, @IsExempt, @IsEmployee )&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationName&quot;, classification.EmployeeClassificationName);
            cmd.Parameters.AddWithValue(&quot;@IsExempt&quot;, classification.IsExempt);
            cmd.Parameters.AddWithValue(&quot;@IsEmployee&quot;, classification.IsEmployee);
            return (int)cmd.ExecuteScalar();
        }
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        const string sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationKey&quot;, classification.EmployeeClassificationKey);
            cmd.ExecuteNonQuery();
        }
    }

    public void DeleteByKey(int employeeClassificationKey)
    {
        const string sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationKey&quot;, employeeClassificationKey);
            cmd.ExecuteNonQuery();
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        const string sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationName = @EmployeeClassificationName;&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationName&quot;, employeeClassificationName);
            using (var reader = cmd.ExecuteReader())
            {
                if (!reader.Read())
                    return null;

                return new ReadOnlyEmployeeClassification(
                    reader.GetInt32(reader.GetOrdinal(&quot;EmployeeClassificationKey&quot;)),
                    reader.GetString(reader.GetOrdinal(&quot;EmployeeClassificationName&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsExempt&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsEmployee&quot;))
                );
            }
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        const string sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee FROM HR.EmployeeClassification ec;&quot;;

        var result = new List&lt;ReadOnlyEmployeeClassification&gt;();

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                result.Add(new ReadOnlyEmployeeClassification(
                    reader.GetInt32(reader.GetOrdinal(&quot;EmployeeClassificationKey&quot;)),
                    reader.GetString(reader.GetOrdinal(&quot;EmployeeClassificationName&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsExempt&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsEmployee&quot;))
                ));
            }

            return result.ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        const string sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationKey&quot;, employeeClassificationKey);
            using (var reader = cmd.ExecuteReader())
            {
                if (!reader.Read())
                    return null;

                return new ReadOnlyEmployeeClassification(
                    reader.GetInt32(reader.GetOrdinal(&quot;EmployeeClassificationKey&quot;)),
                    reader.GetString(reader.GetOrdinal(&quot;EmployeeClassificationName&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsExempt&quot;)),
                    reader.GetBoolean(reader.GetOrdinal(&quot;IsEmployee&quot;))
                );
            }
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        const string sql = @&quot;UPDATE HR.EmployeeClassification
                    SET EmployeeClassificationName = @EmployeeClassificationName, IsExempt = @IsExempt, IsEmployee = @IsEmployee
                    WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
        using (var cmd = new SqlCommand(sql, con))
        {
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationKey&quot;, classification.EmployeeClassificationKey);
            cmd.Parameters.AddWithValue(&quot;@EmployeeClassificationName&quot;, classification.EmployeeClassificationName);
            cmd.Parameters.AddWithValue(&quot;@IsExempt&quot;, classification.IsExempt);
            cmd.Parameters.AddWithValue(&quot;@IsEmployee&quot;, classification.IsEmployee);
            cmd.ExecuteNonQuery();
        }
    }
}
</code></pre>

<h2 id="chain">Chain<a class="headerlink" href="#chain" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>Chain natively supports working with immutable objects, no conversions are needed.</p>
<p>To populate immutable objects, use either the <code>InferConstructor</code> option or a <code>.WithConstructor&lt;...&gt;</code> link to indicate that a non-default constructor should be used.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    readonly SqlServerDataSource m_DataSource;

    public ImmutableScenario(SqlServerDataSource dataSource)
    {
        m_DataSource = dataSource;
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        return m_DataSource.Insert(classification).ToInt32().Execute();
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        m_DataSource.Delete(classification).Execute();
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        return m_DataSource.From&lt;ReadOnlyEmployeeClassification&gt;(new { employeeClassificationName })
            .ToObjectOrNull(RowOptions.InferConstructor).Execute();
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        return m_DataSource.From&lt;ReadOnlyEmployeeClassification&gt;()
            .ToImmutableArray(CollectionOptions.InferConstructor).Execute();
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        return m_DataSource.GetByKey&lt;ReadOnlyEmployeeClassification&gt;(employeeClassificationKey)
            .ToObjectOrNull&lt;ReadOnlyEmployeeClassification&gt;(RowOptions.InferConstructor).Execute();
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        m_DataSource.Update(classification).Execute();
    }
}
</code></pre>

<h2 id="dapper">Dapper<a class="headerlink" href="#dapper" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>Dapper natively supports working with immutable objects, no conversions are needed.</p>
<p>No special handling is needed to call a non-default constructor.</p>
<pre><code class="cs">public class ImmutableScenario : ScenarioBase, IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public ImmutableScenario(string connectionString) : base(connectionString)
    {
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        var sql = @&quot;INSERT INTO HR.EmployeeClassification (EmployeeClassificationName, IsExempt, IsEmployee)
                    OUTPUT Inserted.EmployeeClassificationKey
                    VALUES(@EmployeeClassificationName, @IsExempt, @IsEmployee )&quot;;

        using (var con = OpenConnection())
            return con.ExecuteScalar&lt;int&gt;(sql, classification);
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        var sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
            con.Execute(sql, classification);
    }

    public void DeleteByKey(int employeeClassificationKey)
    {
        var sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
            con.Execute(sql, new { employeeClassificationKey });
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        var sql = @&quot;SELECT  ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationName = @EmployeeClassificationName;&quot;;

        using (var con = OpenConnection())
            return con.QuerySingleOrDefault&lt;ReadOnlyEmployeeClassification&gt;(sql, new { employeeClassificationName });
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        var sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee FROM HR.EmployeeClassification ec;&quot;;

        using (var con = OpenConnection())
            return con.Query&lt;ReadOnlyEmployeeClassification&gt;(sql).ToImmutableList();
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        var sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
            return con.QuerySingle&lt;ReadOnlyEmployeeClassification&gt;(sql, new { employeeClassificationKey });
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        var sql = @&quot;UPDATE HR.EmployeeClassification
                    SET EmployeeClassificationName = @EmployeeClassificationName, IsExempt = @IsExempt, IsEmployee = @IsEmployee
                    WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        using (var con = OpenConnection())
            con.Execute(sql, classification);
    }
}
</code></pre>

<h2 id="dbconnector">DbConnector<a class="headerlink" href="#dbconnector" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>DbConnector currently does not support direct constructor mapping.</p>
<p>Built-in functionality, including extensions, can simply be leveraged when working with immutable objects.</p>
<pre><code class="cs">public class ImmutableScenario : ScenarioBase, IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public ImmutableScenario(string connectionString) : base(connectionString)
    {
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        const string sql = @&quot;INSERT INTO HR.EmployeeClassification (EmployeeClassificationName, IsExempt, IsEmployee)
                    OUTPUT Inserted.EmployeeClassificationKey
                    VALUES(@EmployeeClassificationName, @IsExempt, @IsEmployee);&quot;;

        return DbConnector.Scalar&lt;int&gt;(sql, classification).Execute();
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        var sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        DbConnector.NonQuery(sql, classification).Execute();
    }

    public void DeleteByKey(int employeeClassificationKey)
    {
        var sql = @&quot;DELETE HR.EmployeeClassification WHERE EmployeeClassificationKey = @employeeClassificationKey;&quot;;

        DbConnector.NonQuery(sql, new { employeeClassificationKey }).Execute();
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        var sql = @&quot;SELECT  ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationName = @employeeClassificationName;&quot;;

        //DbConnector currently does not support direct constructor mapping (v1.2.1)
        return DbConnector.ReadTo&lt;ReadOnlyEmployeeClassification&gt;(
            sql: sql,
            param: new { employeeClassificationName },
            onLoad: (ReadOnlyEmployeeClassification result, IDbExecutionModel em, DbDataReader odr) =&gt;
            {
                //Leverage extension from &quot;DbConnector.Core.Extensions&quot;
                dynamic row = odr.SingleOrDefault(em.Token, em.JobCommand);

                if (row != null)
                    result = new ReadOnlyEmployeeClassification(row.EmployeeClassificationKey, row.EmployeeClassificationName, row.IsExempt, row.IsEmployee);

                //Alternative: Extract values from the DbDataReader manually
                //if (odr.HasRows &amp;&amp; odr.Read())
                //{
                //    int employeeClassificationKey = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationKey)) as int? ?? 0;
                //    string employeeClassificationName = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationName)) as string;
                //    bool isExempt = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsExempt)) as bool? ?? false;
                //    bool isEmployee = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsEmployee)) as bool? ?? false;

                //    result = new ReadOnlyEmployeeClassification(employeeClassificationKey, employeeClassificationName, isExempt, isEmployee);

                //    if (odr.Read())//SingleOrDefault behavior
                //    {
                //        throw new InvalidOperationException(&quot;The query result has more than one result.&quot;);
                //    }
                //}

                return result;
            })
            .Execute();
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        var sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee FROM HR.EmployeeClassification ec;&quot;;

        //DbConnector currently does not support direct constructor mapping (v1.2.1)
        return DbConnector.ReadTo&lt;List&lt;ReadOnlyEmployeeClassification&gt;&gt;(
            sql: sql,
            param: null,
            onLoad: (List&lt;ReadOnlyEmployeeClassification&gt; result, IDbExecutionModel em, DbDataReader odr) =&gt;
            {
                result = new List&lt;ReadOnlyEmployeeClassification&gt;();

                //Extract values from the DbDataReader manually
                if (odr.HasRows)
                {
                    while (odr.Read())
                    {
                        if (em.Token.IsCancellationRequested)
                            return result;

                        int employeeClassificationKey = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationKey)) as int? ?? 0;
                        string employeeClassificationName = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationName)) as string;
                        bool isExempt = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsExempt)) as bool? ?? false;
                        bool isEmployee = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsEmployee)) as bool? ?? false;

                        result.Add(new ReadOnlyEmployeeClassification(employeeClassificationKey, employeeClassificationName, isExempt, isEmployee));
                    }
                }

                return result;
            })
            .Execute()
            .ToImmutableList();
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        var sql = @&quot;SELECT ec.EmployeeClassificationKey, ec.EmployeeClassificationName, ec.IsExempt, ec.IsEmployee
                    FROM HR.EmployeeClassification ec
                    WHERE ec.EmployeeClassificationKey = @employeeClassificationKey;&quot;;

        //DbConnector currently does not support direct constructor mapping (v1.2.1)
        return DbConnector.ReadTo&lt;ReadOnlyEmployeeClassification&gt;(
            sql: sql,
            param: new { employeeClassificationKey },
            onLoad: (ReadOnlyEmployeeClassification result, IDbExecutionModel em, DbDataReader odr) =&gt;
            {
                //Leverage extension from &quot;DbConnector.Core.Extensions&quot;
                dynamic row = odr.Single(em.Token, em.JobCommand);

                if (row != null)
                    result = new ReadOnlyEmployeeClassification(row.EmployeeClassificationKey, row.EmployeeClassificationName, row.IsExempt, row.IsEmployee);

                //Alternative: Extract values from the DbDataReader manually
                //if (odr.HasRows &amp;&amp; odr.Read())
                //{
                //    int employeeClassificationKey = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationKey)) as int? ?? 0;
                //    string employeeClassificationName = odr.GetValue(nameof(ReadOnlyEmployeeClassification.EmployeeClassificationName)) as string;
                //    bool isExempt = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsExempt)) as bool? ?? false;
                //    bool isEmployee = odr.GetValue(nameof(ReadOnlyEmployeeClassification.IsEmployee)) as bool? ?? false;

                //    result = new ReadOnlyEmployeeClassification(employeeClassificationKey, employeeClassificationName, isExempt, isEmployee);

                //    if (odr.Read())//Single behavior
                //    {
                //        throw new InvalidOperationException(&quot;The query result has more than one result.&quot;);
                //    }
                //}
                //else
                //{
                //    //Single behavior
                //    throw new InvalidOperationException(&quot;The query result is empty.&quot;);
                //}

                return result;
            })
            .Execute();
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        var sql = @&quot;UPDATE HR.EmployeeClassification
                    SET EmployeeClassificationName = @EmployeeClassificationName, IsExempt = @IsExempt, IsEmployee = @IsEmployee
                    WHERE EmployeeClassificationKey = @EmployeeClassificationKey;&quot;;

        DbConnector.NonQuery(sql, classification).Execute();
    }
}
</code></pre>

<h2 id="entity-framework-6">Entity Framework 6<a class="headerlink" href="#entity-framework-6" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>Entity Framework does not directly support immutable objects. You can overcome this by using a pair of conversions between the immutable object and the mutable entity.</p>
<p>Objects need to be materialized client-side before being mapped to the immutable type.</p>
<pre><code class="cs">public ReadOnlyEmployeeClassification(EmployeeClassification entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity)} is null.&quot;);
    if (entity.EmployeeClassificationName == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.EmployeeClassificationName)} is null.&quot;);

    EmployeeClassificationKey = entity.EmployeeClassificationKey;
    EmployeeClassificationName = entity.EmployeeClassificationName;
    IsExempt = entity.IsExempt;
    IsEmployee = entity.IsEmployee;
}
</code></pre>

<pre><code class="cs">public EmployeeClassification ToEntity()
{
    return new EmployeeClassification()
    {
        EmployeeClassificationKey = EmployeeClassificationKey,
        EmployeeClassificationName = EmployeeClassificationName,
        IsExempt = IsExempt,
        IsEmployee = IsEmployee
    };
}
</code></pre>

<p>These conversions are used in the repository before write operations and after read operations.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    private Func&lt;OrmCookbookContext&gt; CreateDbContext;

    public ImmutableScenario(Func&lt;OrmCookbookContext&gt; dBContextFactory)
    {
        CreateDbContext = dBContextFactory;
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            var temp = classification.ToEntity();
            context.EmployeeClassification.Add(temp);
            context.SaveChanges();
            return temp.EmployeeClassificationKey;
        }
    }

    public virtual void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            //Find the row you wish to delete
            var temp = context.EmployeeClassification.Find(classification.EmployeeClassificationKey);
            if (temp != null)
            {
                context.EmployeeClassification.Remove(temp);
                context.SaveChanges();
            }
        }
    }

    public virtual void DeleteByKey(int employeeClassificationKey)
    {
        using (var context = CreateDbContext())
        {
            //Find the row you wish to delete
            var temp = context.EmployeeClassification.Find(employeeClassificationKey);
            if (temp != null)
            {
                context.EmployeeClassification.Remove(temp);
                context.SaveChanges();
            }
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var context = CreateDbContext())
        {
            return context.EmployeeClassification
                .Where(ec =&gt; ec.EmployeeClassificationName == employeeClassificationName)
                .ToList() //everything below this line is client-side
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).SingleOrDefault();
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var context = CreateDbContext())
        {
            return context.EmployeeClassification
                .ToList() //everything below this line is client-side
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification GetByKey(int employeeClassificationKey)
    {
        using (var context = CreateDbContext())
        {
            var temp = context.EmployeeClassification.Find(employeeClassificationKey);
            if (temp == null)
                throw new DataException($&quot;No row was found for key {employeeClassificationKey}.&quot;);
            return new ReadOnlyEmployeeClassification(temp);
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            //Get a fresh copy of the row from the database
            var temp = context.EmployeeClassification.Find(classification.EmployeeClassificationKey);
            if (temp != null)
            {
                //Copy the changed fields
                temp.EmployeeClassificationName = classification.EmployeeClassificationName;
                temp.IsEmployee = classification.IsEmployee;
                temp.IsExempt = classification.IsExempt;
                context.SaveChanges();
            }
        }
    }
}
</code></pre>

<h2 id="entity-framework-core">Entity Framework Core<a class="headerlink" href="#entity-framework-core" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>Entity Framework Core does not directly support immutable objects. You can overcome this by using a pair of conversions between the immutable object and the mutable entity.</p>
<pre><code class="cs">public ReadOnlyEmployeeClassification(EmployeeClassification entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity)} is null.&quot;);
    if (entity.EmployeeClassificationName == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.EmployeeClassificationName)} is null.&quot;);
    if (entity.IsEmployee == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.IsEmployee)} is null.&quot;);

    EmployeeClassificationKey = entity.EmployeeClassificationKey;
    EmployeeClassificationName = entity.EmployeeClassificationName;
    IsExempt = entity.IsExempt;
    IsEmployee = entity.IsEmployee.Value;
}
</code></pre>

<pre><code class="cs">public EmployeeClassification ToEntity()
{
    return new EmployeeClassification()
    {
        EmployeeClassificationKey = EmployeeClassificationKey,
        EmployeeClassificationName = EmployeeClassificationName,
        IsExempt = IsExempt,
        IsEmployee = IsEmployee
    };
}
</code></pre>

<p>These conversions are used in the repository before write operations and after read operations.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    private Func&lt;OrmCookbookContext&gt; CreateDbContext;

    public ImmutableScenario(Func&lt;OrmCookbookContext&gt; dBContextFactory)
    {
        CreateDbContext = dBContextFactory;
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            var temp = classification.ToEntity();
            context.EmployeeClassifications.Add(temp);
            context.SaveChanges();
            return temp.EmployeeClassificationKey;
        }
    }

    public virtual void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            //Find the row you wish to delete
            var temp = context.EmployeeClassifications.Find(classification.EmployeeClassificationKey);
            if (temp != null)
            {
                context.EmployeeClassifications.Remove(temp);
                context.SaveChanges();
            }
        }
    }

    public virtual void DeleteByKey(int employeeClassificationKey)
    {
        using (var context = CreateDbContext())
        {
            //Find the row you wish to delete
            var temp = context.EmployeeClassifications.Find(employeeClassificationKey);
            if (temp != null)
            {
                context.EmployeeClassifications.Remove(temp);
                context.SaveChanges();
            }
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var context = CreateDbContext())
        {
            return context.EmployeeClassifications
                .Where(ec =&gt; ec.EmployeeClassificationName == employeeClassificationName)
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).SingleOrDefault();
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var context = CreateDbContext())
        {
            return context.EmployeeClassifications.Select(x =&gt; new ReadOnlyEmployeeClassification(x)).ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification GetByKey(int employeeClassificationKey)
    {
        using (var context = CreateDbContext())
        {
            var temp = context.EmployeeClassifications.Find(employeeClassificationKey);
            if (temp == null)
                throw new DataException($&quot;No row was found for key {employeeClassificationKey}.&quot;);
            return new ReadOnlyEmployeeClassification(temp);
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var context = CreateDbContext())
        {
            //Get a fresh copy of the row from the database
            var temp = context.EmployeeClassifications.Find(classification.EmployeeClassificationKey);
            if (temp != null)
            {
                //Copy the changed fields
                temp.EmployeeClassificationName = classification.EmployeeClassificationName;
                temp.IsEmployee = classification.IsEmployee;
                temp.IsExempt = classification.IsExempt;
                context.SaveChanges();
            }
        }
    }
}
</code></pre>

<h2 id="linq-to-db">LINQ to DB<a class="headerlink" href="#linq-to-db" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>LINQ to DB does not directly support immutable objects. You can overcome this by using a pair of conversions between the immutable object and the mutable entity.</p>
<pre><code class="cs">public ReadOnlyEmployeeClassification(EmployeeClassification entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity)} is null.&quot;);
    if (entity.EmployeeClassificationName == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.EmployeeClassificationName)} is null.&quot;);

    EmployeeClassificationKey = entity.EmployeeClassificationKey;
    EmployeeClassificationName = entity.EmployeeClassificationName;
    IsExempt = entity.IsExempt;
    IsEmployee = entity.IsEmployee;
}
</code></pre>

<pre><code class="cs">public EmployeeClassification ToEntity()
{
    return new EmployeeClassification()
    {
        EmployeeClassificationKey = EmployeeClassificationKey,
        EmployeeClassificationName = EmployeeClassificationName,
        IsExempt = IsExempt,
        IsEmployee = IsEmployee
    };
}
</code></pre>

<p>These conversions are used in the repository before write operations and after read operations.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = new OrmCookbook())
        {
            return db.InsertWithInt32Identity(classification.ToEntity());
        }
    }

    public virtual void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = new OrmCookbook())
        {
            db.EmployeeClassification
                .Where(d =&gt; d.EmployeeClassificationKey == classification.EmployeeClassificationKey)
                .Delete();
        }
    }

    public virtual void DeleteByKey(int employeeClassificationKey)
    {
        using (var db = new OrmCookbook())
        {
            db.EmployeeClassification
                .Where(d =&gt; d.EmployeeClassificationKey == employeeClassificationKey)
                .Delete();
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var db = new OrmCookbook())
        {
            var query = from ec in db.EmployeeClassification
                        where ec.EmployeeClassificationName == employeeClassificationName
                        select ec;
            return query.Select(x =&gt; new ReadOnlyEmployeeClassification(x)).SingleOrDefault();
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var db = new OrmCookbook())
        {
            return db.EmployeeClassification
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification GetByKey(int employeeClassificationKey)
    {
        using (var db = new OrmCookbook())
        {
            return db.EmployeeClassification.Where(d =&gt; d.EmployeeClassificationKey == employeeClassificationKey)
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).Single();
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = new OrmCookbook())
        {
            db.Update(classification.ToEntity());
        }
    }
}
</code></pre>

<h2 id="llblgen-pro">LLBLGen Pro<a class="headerlink" href="#llblgen-pro" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>LLBLGen Pro supports 'action' specifications on entities, e.g. an entity can only be fetched, or fetched and updated, but e.g. not deleted. An entity that's marked as 'Read' can't be updated, deleted or inserted. The scope of the recipes in this cookbook however
focus on immutable data in-memory. LLBLGen Pro does not directly support these objects, You can overcome this by using a pair of conversions between the immutable object and the mutable entity.</p>
<pre><code class="cs">public ReadOnlyEmployeeClassification(EmployeeClassificationEntity entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity)} is null.&quot;);
    if (entity.EmployeeClassificationName == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.EmployeeClassificationName)} is null.&quot;);

    EmployeeClassificationKey = entity.EmployeeClassificationKey;
    EmployeeClassificationName = entity.EmployeeClassificationName;
    IsExempt = entity.IsExempt;
    IsEmployee = entity.IsEmployee;
}
</code></pre>

<pre><code class="cs">public EmployeeClassificationEntity ToEntity()
{
    return new EmployeeClassificationEntity()
    {
        EmployeeClassificationKey = EmployeeClassificationKey,
        EmployeeClassificationName = EmployeeClassificationName,
        IsExempt = IsExempt,
        IsEmployee = IsEmployee
    };
}
</code></pre>

<p>These conversions are used in the repository before write operations and after read operations.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var adapter = new DataAccessAdapter())
        {
            var toPersist = classification.ToEntity();
            adapter.SaveEntity(toPersist);
            return toPersist.EmployeeClassificationKey;
        }
    }

    public virtual void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        DeleteByKey(classification.EmployeeClassificationKey);
    }

    public virtual void DeleteByKey(int employeeClassificationKey)
    {
        using (var adapter = new DataAccessAdapter())
        {
            adapter.DeleteEntitiesDirectly(typeof(EmployeeClassificationEntity),
                                           new RelationPredicateBucket(EmployeeClassificationFields.EmployeeClassificationKey
                                                                                       .Equal(employeeClassificationKey)));
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var adapter = new DataAccessAdapter())
        {
            return new LinqMetaData(adapter).EmployeeClassification
                                                .Where(ec =&gt; ec.EmployeeClassificationName == employeeClassificationName)
                                                .Select(x =&gt; new ReadOnlyEmployeeClassification(x))
                                    .SingleOrDefault();
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var adapter = new DataAccessAdapter())
        {
            return new LinqMetaData(adapter).EmployeeClassification.Select(x =&gt; new ReadOnlyEmployeeClassification(x)).ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification GetByKey(int employeeClassificationKey)
    {
        using (var adapter = new DataAccessAdapter())
        {
            var temp = adapter.FetchNewEntity&lt;EmployeeClassificationEntity&gt;(
                                            new RelationPredicateBucket(EmployeeClassificationFields.EmployeeClassificationKey
                                                                                                    .Equal(employeeClassificationKey)));
            if (temp.IsNew)
                throw new DataException($&quot;No row was found for key {employeeClassificationKey}.&quot;);
            return new ReadOnlyEmployeeClassification(temp);
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var adapter = new DataAccessAdapter())
        {
            //Get a fresh copy of the row from the database
            var temp = adapter.FetchNewEntity&lt;EmployeeClassificationEntity&gt;(
                                new RelationPredicateBucket(EmployeeClassificationFields.EmployeeClassificationKey
                                                                            .Equal(classification.EmployeeClassificationKey)));
            if (!temp.IsNew)
            {
                //Copy the changed fields
                temp.EmployeeClassificationName = classification.EmployeeClassificationName;
                temp.IsEmployee = classification.IsEmployee;
                temp.IsExempt = classification.IsExempt;
                adapter.SaveEntity(temp);
            }
        }
    }
}
</code></pre>

<h2 id="nhibernate">NHibernate<a class="headerlink" href="#nhibernate" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>NHibernate does not directly support immutable objects. You can overcome this by using a pair of conversions between the immutable object and the mutable entity.</p>
<pre><code class="cs">public ReadOnlyEmployeeClassification(EmployeeClassification entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity)} is null.&quot;);
    if (entity.EmployeeClassificationName == null)
        throw new ArgumentNullException(nameof(entity), $&quot;{nameof(entity.EmployeeClassificationName)} is null.&quot;);

    EmployeeClassificationKey = entity.EmployeeClassificationKey;
    EmployeeClassificationName = entity.EmployeeClassificationName;
    IsExempt = entity.IsExempt;
    IsEmployee = entity.IsEmployee;
}
</code></pre>

<pre><code class="cs">public EmployeeClassification ToEntity()
{
    return new EmployeeClassification()
    {
        EmployeeClassificationKey = EmployeeClassificationKey,
        EmployeeClassificationName = EmployeeClassificationName,
        IsExempt = IsExempt,
        IsEmployee = IsEmployee
    };
}
</code></pre>

<p>These conversions are used in the repository before write operations and after read operations.</p>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    readonly ISessionFactory m_SessionFactory;

    public ImmutableScenario(ISessionFactory sessionFactory)
    {
        m_SessionFactory = sessionFactory;
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var session = m_SessionFactory.OpenSession())
        {
            var temp = classification.ToEntity();
            session.Save(temp);
            session.Flush();
            return temp.EmployeeClassificationKey;
        }
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var session = m_SessionFactory.OpenSession())
        {
            session.Delete(classification.ToEntity());
            session.Flush();
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var session = m_SessionFactory.OpenStatelessSession())
        {
            return session.QueryOver&lt;EmployeeClassification&gt;().Where(e =&gt; e.EmployeeClassificationName == employeeClassificationName).List()
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).SingleOrDefault();
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var session = m_SessionFactory.OpenStatelessSession())
        {
            return session.QueryOver&lt;EmployeeClassification&gt;().List()
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x)).ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        using (var session = m_SessionFactory.OpenStatelessSession())
        {
            var result = session.Get&lt;EmployeeClassification&gt;(employeeClassificationKey);
            return new ReadOnlyEmployeeClassification(result);
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var session = m_SessionFactory.OpenSession())
        {
            session.Update(classification.ToEntity());
            session.Flush();
        }
    }
}
</code></pre>

<h2 id="repodb">RepoDb<a class="headerlink" href="#repodb" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<p>RepoDb does not directly support immutable objects. You have to manage the conversion between <em>mutable</em> and <em>immutable</em> objects in order to make it work.</p>
<p>Below is a sample snippet for <em>immutable</em> class.</p>
<pre><code class="cs">[Map(&quot;[HR].[EmployeeClassification]&quot;)]
public class ReadOnlyEmployeeClassification : IReadOnlyEmployeeClassification
{
    public ReadOnlyEmployeeClassification(EmployeeClassification classification)
    {
        if (classification?.EmployeeClassificationName == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification.EmployeeClassificationName)} is null.&quot;);

        EmployeeClassificationKey = classification.EmployeeClassificationKey;
        EmployeeClassificationName = classification.EmployeeClassificationName;
        IsExempt = classification.IsExempt;
        IsEmployee = classification.IsEmployee;
    }

    public ReadOnlyEmployeeClassification(int employeeClassificationKey,
        string employeeClassificationName,
        bool isExempt,
        bool isEmployee)
    {
        EmployeeClassificationKey = employeeClassificationKey;
        EmployeeClassificationName = employeeClassificationName;
        IsExempt = isExempt;
        IsEmployee = isEmployee;
    }

    public int EmployeeClassificationKey { get; }

    public string EmployeeClassificationName { get; }
    public bool IsEmployee { get; }
    public bool IsExempt { get; }
}
</code></pre>

<p>Below is a sample snippet for <em>mutable</em> class.</p>
<pre><code class="cs">[Map(&quot;[HR].[EmployeeClassification]&quot;)]
public class EmployeeClassification : IEmployeeClassification
{
    public EmployeeClassification()
    {
    }

    public EmployeeClassification(IReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        EmployeeClassificationKey = classification.EmployeeClassificationKey;
        EmployeeClassificationName = classification.EmployeeClassificationName;
        IsExempt = classification.IsExempt;
        IsEmployee = classification.IsEmployee;
    }

    public int EmployeeClassificationKey { get; set; }

    public string? EmployeeClassificationName { get; set; }

    public bool IsEmployee { get; set; }

    public bool IsExempt { get; set; }

    internal ReadOnlyEmployeeClassification ToImmutable()
    {
        return new ReadOnlyEmployeeClassification(this);
    }
}
</code></pre>

<p>Below is the <em>immutable repository</em>.</p>
<pre><code class="cs">public class ImmutableScenario : BaseRepository&lt;ReadOnlyEmployeeClassification, SqlConnection&gt;,
    IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    public ImmutableScenario(string connectionString)
        : base(connectionString, RDB.Enumerations.ConnectionPersistency.Instance)
    { }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        return Insert&lt;int&gt;(classification);
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        base.Delete(classification);
    }

    public void DeleteByKey(int employeeClassificationKey)
    {
        Delete(employeeClassificationKey);
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        return Query(e =&gt; e.EmployeeClassificationName == employeeClassificationName)
            .FirstOrDefault();
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        return QueryAll()
            .ToImmutableList();
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        return Query(employeeClassificationKey)
            .FirstOrDefault();
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        base.Update(classification);
    }
}
</code></pre>

<h2 id="servicestack">ServiceStack<a class="headerlink" href="#servicestack" title="Permalink to this headline"><i class="fa fa-link" aria-hidden="true"></i></a></h2>
<pre><code class="cs">public class ImmutableScenario : IImmutableScenario&lt;ReadOnlyEmployeeClassification&gt;
{
    private readonly IDbConnectionFactory _dbConnectionFactory;

    public ImmutableScenario(IDbConnectionFactory dbConnectionFactory)
    {
        _dbConnectionFactory = dbConnectionFactory;
    }

    public int Create(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            return (int)db.Insert(new EmployeeClassification().PopulateWith(classification), true);
        }
    }

    public void Delete(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            var deleted = db.Delete&lt;EmployeeClassification&gt;(r =&gt; r.Id == classification.Id);
            if (deleted != 1)
                throw new DataException($&quot;No row was found for key {classification.EmployeeClassificationKey}.&quot;);
        }
    }

    public void DeleteByKey(int employeeClassificationKey)
    {
        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            var deleted = db.Delete&lt;EmployeeClassification&gt;(r =&gt; r.Id == employeeClassificationKey);
            if (deleted != 1)
                throw new DataException($&quot;No row was found for key {employeeClassificationKey}.&quot;);
        }
    }

    public ReadOnlyEmployeeClassification? FindByName(string employeeClassificationName)
    {
        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            var temp = db.Single&lt;EmployeeClassification&gt;(r =&gt;
                r.EmployeeClassificationName == employeeClassificationName);
            return temp == null ? null : new ReadOnlyEmployeeClassification(temp);
        }
    }

    public IReadOnlyList&lt;ReadOnlyEmployeeClassification&gt; GetAll()
    {
        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            return db.Select&lt;EmployeeClassification&gt;()
                .Select(x =&gt; new ReadOnlyEmployeeClassification(x))
                .ToImmutableArray();
        }
    }

    public ReadOnlyEmployeeClassification? GetByKey(int employeeClassificationKey)
    {
        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            var temp = db.Single&lt;EmployeeClassification&gt;(r =&gt;
                r.Id == employeeClassificationKey);
            if (temp == null)
                throw new DataException($&quot;No row was found for key {employeeClassificationKey}.&quot;);
            return new ReadOnlyEmployeeClassification(temp);
        }
    }

    public void Update(ReadOnlyEmployeeClassification classification)
    {
        if (classification == null)
            throw new ArgumentNullException(nameof(classification), $&quot;{nameof(classification)} is null.&quot;);

        using (var db = _dbConnectionFactory.OpenDbConnection())
        {
            db.Update&lt;EmployeeClassification&gt;(new
            {
                classification.IsEmployee,
                classification.IsExempt,
                classification.EmployeeClassificationName
            }, r =&gt; r.Id == classification.Id);
        }
    }
}
</code></pre>


                    </div>
                </div>
                <footer>
                    <hr />
                    <div role="contentinfo">
The ORM Cookbook. <a href='https://github.com/Grauenwolf/DotNet-ORM-Cookbook' target='_blank'>Visit us at GitHub</a>.
                    </div>
                </footer>
            </div>
        </section>
    </div>
    <script src="js/jquery-2.1.1.min.js"></script>
    <script src="js/modernizr-2.8.3.min.js"></script>
    <script src="js/highlight.pack.js"></script>
    <script src="js/theme.js"></script>

</body>
</html>
